#!/usr/bin/python3
# -*- coding: utf-8 -*-

# @file app_service.py
# @brief
# @author QRS
# @version 1.0
# @date 2021-06-25 16:14


import os, time
import json
import traceback
import argparse
import zmq
import redis
import re

from urllib import parse
from flask import Flask, request
from flask_cors import CORS
from frepai.utils.misc import frep_timeit, frep_subprocess # noqa
from frepai.utils.error import catch_error
from frepai.utils.misc import ( # noqa
        frep_run_shell,
        frep_oss_client,
        frep_object_put)

g_osscli = frep_oss_client()

app = Flask(__name__)
CORS(app, supports_credentials=True)

app.debug = True
app_logger = app.logger.info

context = zmq.Context()
zmqpub = context.socket(zmq.PUB)
g_redis = None
g_ffmpeg = '/usr/local/srs/objs/ffmpeg/bin/ffmpeg'

# mac-uid map

MAC2UID = {
    "00e685ef87b4": "SSAC-462825-DBFBF",
    "00ee44217446": "SSSS-120078-DBAFF",
    "00cf60a8f512": "SSAC-332308-DCBEC",
    "004309167638": "SSAC-292214-CEDDE",
    "006b246f536d": "SSAC-292195-CEDDE",
    "0031059d1d11": "SSAC-292202-FBDEB",
    "002b359e3931": "SSAC-292197-ECFAB",
    "00232ee8876d": "SSAC-292217-AAFFA",
    "00047dd87188": "SSAC-292170-BAAEC",
    "00d51a6becbb": "SSAC-292170-ECFAC",
    "008e9b61ac6c": "SSAA-147882-DFFFD",
    "00335ebc0407": "SSAA-360395-FDBAA",
    "006bc1e9cc71": "SSAE-110454-DCDCC",
}


def _get_username_password(params):
    username, password = 'unkown', 'unkown'
    for key, val in parse.parse_qsl(params):
        if key == 'user':
            username = val
        if key == 'pssw':
            password = val
    return username, password


def _parse_params(params, keys):
    result = {}
    for key, val in parse.parse_qsl(params):
        if key in keys:
            result[key] = val
    return result


@app.route('/apis/srs/v1/on_connect', methods=['POST', 'PUT'])
def _srs_on_connect():
    reqjson = json.loads(request.get_data().decode())
    if reqjson['stream'] in MAC2UID.keys():
        reqjson['uid'] = MAC2UID[reqjson['stream']]
    app_logger(reqjson)
    return '0'


@app.route('/apis/srs/v1/on_close', methods=['POST'])
def _srs_on_close():
    reqjson = json.loads(request.get_data().decode())
    # if reqjson['stream'] in MAC2UID.keys():
    #     reqjson['uid'] = MAC2UID[reqjson['stream']]
    app_logger(reqjson)
    return '0'


@app.route('/apis/srs/v1/on_publish', methods=['POST'])
def _srs_on_publish():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    g_redis.lpush('factory:' + reqjson['action'], json.dumps(reqjson))
    # if len(reqjson['param']) == 0:
    #     return '-1'
    # username, password = _get_username_password(reqjson['param'][1:])
    # TODO
    # if username == 'talentai' and password == '123456':
    #     return '0'
    return '0'


@app.route('/apis/srs/v1/on_unpublish', methods=['POST'])
def _srs_on_unpublish():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    g_redis.lpush('factory:' + reqjson['action'], json.dumps(reqjson))
    return '0'


@app.route('/apis/srs/v1/on_play', methods=['POST'])
def _srs_on_play():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    return '0'


@app.route('/apis/srs/v1/on_stop', methods=['POST'])
def _srs_on_stop():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    return '0'


@app.route('/apis/srs/v1/on_dvr', methods=['POST'])
@catch_error
@frep_timeit(app_logger)
def _srs_on_dvr():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    # {
    #     "action": "on_dvr",
    #     "client_id": 156,
    #     "ip": "36.110.84.59",
    #     "vhost": "seg.30s",
    #     "app": "f01",
    #     "stream": "00e685ef87b4",
    #     "param": "?vhost=seg.30s",
    #     "cwd": "/usr/local/srs",
    #     "file": "/frepai/data/f01/00e685ef87b4/20210629170921.mp4"
    # }
    # params = _parse_params(reqjson['param'][1:], ['workshop', 'region'])
    # if len(params) != 2:
    #     return '-1'
    basefile = os.path.basename(reqjson['file'])
    localfile = os.path.join(reqjson['cwd'], reqjson['file'])
    localpath = os.path.dirname(localfile)
    remotepath = os.path.join(reqjson['app'], reqjson['stream'], basefile[:8], 'videos')
    prefix_map = [localpath, remotepath]

    output = frep_run_shell(f'{g_ffmpeg} -i {localfile}')
    result = re.search(r'Duration: (?P<hours>\d+):(?P<minutes>\d+):(?P<seconds>\d+)\.\d+,', output)
    if result:
        timesecs = result.groupdict()
        duration = 3600 * int(timesecs['hours']) + 60 * int(timesecs['minutes']) + int(timesecs['seconds'])
    else:
        if 'seg' in reqjson['vhost']:
            duration = int(reqjson['vhost'].split('.')[1][:-1])
        else:
            duration = -1

    if duration <= 0:
        app_logger('Error: The dvr video have no frames!')
        return '0'

    result = frep_object_put(g_osscli, localfile, prefix_map=prefix_map)
    if len(result) == 1 and 'object' in result[0]:
        result[0]['app'] = reqjson['app']
        result[0]['stream'] = reqjson['stream']
        if reqjson['stream'] in MAC2UID.keys():
            result[0]['uid'] = MAC2UID[reqjson['stream']]
        result[0]['filename'] = basefile
        result[0]['duration'] = duration
        app_logger(result[0])
        g_redis.lpush('factory:' + reqjson['action'], json.dumps(result[0]))
    # shutil.rmtree(localpath)
    os.remove(localfile)
    return '0'


@app.route('/apis/srs/v1/on_hls', methods=['POST'])
def _srs_on_hls():
    reqjson = json.loads(request.get_data().decode())
    app_logger(reqjson)
    return '0'


@app.route('/apis/srs/v1/on_hls_notify', methods=['GET'])
def _srs_on_hls_notify():
    app_logger(request.args)
    return '0'


@app.route('/frepai/framework/inference', methods=['POST'], endpoint='inference')
@catch_error
def _framework_inference():
    reqjson = json.loads(request.get_data().decode())

    #### debug
    # if not os.path.exists('/frepai/data/tmp'):
    #     os.makedirs('/frepai/data/tmp')
    # with open('/frepai/data/tmp/frepai.inference.json', 'w') as fw:
    #     json.dump(reqjson, fw)
    ####

    if reqjson['task'].startswith('zmq'):
        cfg = reqjson['cfg']
        if isinstance(cfg, str):
            cfg = json.loads(cfg)

        # app_logger(cfg)
        # TODO
        msgkey = cfg['pigeon']['msgkey']
        if msgkey[:2] == 'nb': # notebook call
            task_topic = f'{reqjson["task"]}_{msgkey}'
        else:
            task_topic = reqjson['task']
        for i in range(5):
            res = g_redis.get(task_topic)
            if res is None or res.decode() != '1':
                break
            if i == 4:
                app_logger('error: task already run.')
                g_redis.delete(task_topic)
                # return json.dumps({'errno': -3, 'errtxt': 'task already run'})
            time.sleep(0.3)
        try:
            len = g_redis.llen(msgkey)
            if len > 0:
                app_logger(f'error: redis key {msgkey} length is not 0: {len}')
            g_redis.delete(msgkey)
            zmqpub.send_string('%s %s' % (reqjson['task'], json.dumps(cfg, separators=(',',':'))))
        except Exception:
            app_logger('error: task run error.')
            return json.dumps({'errno': -4, 'errtxt': traceback.format_exc(limit=3)})
        return json.dumps({'errno': 0})
    return json.dumps({'errno': -5})


@app.route('/frepai/private/pushmsg', methods=['POST', 'GET'], endpoint='pushmsg')
@catch_error
def _framework_message_push():
    try:
        key = request.args.get("key", default='unknown')
        val = request.get_data().decode()
        if key == 'zmp_run':
            topic, secs = val.split(':')
            g_redis.getset(topic, 1)
            g_redis.expire(topic, secs)
        elif key == 'zmp_end':
            g_redis.delete(val)
        else:
            if g_redis:
                g_redis.lpush(key, val)
                g_redis.expire(key, 3600) # 1 days
    except Exception as err:
        app_logger(err)
        return "-1"
    return "0"


@app.route('/frepai/private/popmsg', methods=['GET'])
def _framework_message_pop():
    response = []
    try:
        if g_redis:
            key = request.args.get("key", default='unknown')
            while True:
                item = g_redis.rpop(key)
                if item is None:
                    break
                response.append(json.loads(item.decode()))
    except Exception as err:
        app_logger(err)
    finally:
        return json.dumps(response)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
            '--host',
            default='0.0.0.0',
            type=str,
            dest='host',
            help="host to run frepai service")
    parser.add_argument(
            '--debug',
            default=0,
            type=int,
            dest='debug',
            help="debug mode")
    parser.add_argument(
            '--port',
            default=9119,
            type=int,
            dest='port',
            help="port to run frepai service")
    parser.add_argument(
            '--redis_addr',
            default=None,
            type=str,
            dest='redis_addr',
            help="redis address")
    parser.add_argument(
            '--redis_port',
            default=10090,
            type=int,
            dest='redis_port',
            help="redis port")
    parser.add_argument(
            '--redis_passwd',
            default='123456',
            type=str,
            dest='redis_passwd',
            help="redis passwd")

    args = parser.parse_args()

    try:
        zmqpub.bind("tcp://*:5555")
        g_redis = redis.StrictRedis(args.redis_addr,
                port=args.redis_port,
                password=args.redis_passwd)
    except Exception as err:
        app_logger('{}'.format(err))
    try:
        # debug == 1: auto reload
        app.run(host=args.host, port=args.port, debug=bool(args.debug))
    finally:
        pass
